/**
 * Copyright 2018 orivil.com. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package admin

import (
	"gitee.com/tugoer/orivil/mux"
	"encoding/json"
	"strconv"
	"gitee.com/tugoer/orivil/log"
	"gitee.com/tugoer/orivil/trace"
	"sync"
	"gitee.com/tugoer/orivil/session"
)

const (
	RouteAdminLogin = "admin/login"
	RouteAdminInfo = "admin/info"
)

var Actions = []*mux.Action{
	{
		Name: "Admin Info",
		Method:"GET",
		Route: RouteAdminInfo,
		Handler: func(ctx *mux.Context) {
			a := GetAdminFromSession(ctx)
			ctx.SendJson(a)
		},
		ApiDoc:&mux.ApiDoc{
			Tags:[]string{tagAdmin},
			Summary:"获得当前登陆的管理员信息",
			Responses:mux.Responses{
				"200":{
					Description:"success",
					Example:Admin{},
				},
			},
		},
	},
	{
		Name: "Admin Logout",
		Method:"GET",
		Route: "admin/logout",
		Handler: func(ctx *mux.Context) {
			DelAdminFromSession(ctx)
			redirect := ctx.Query().Get("redirect")
			if redirect == "" {
				redirect = "/"
			}
			ctx.Redirect(redirect)
		},
		ApiDoc:&mux.ApiDoc{
			Tags:[]string{tagAdmin},
			Summary:"退出登陆",
		},
	},
	{
		Name: "Admin Login",
		Method:"POST",
		Route: RouteAdminLogin,
		Params:mux.Parameters{
			{
				In:mux.Form,
				Schema:&loginAdmin{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &loginAdmin{}
			ctx.WarnParse(p)
			a, err := AdminLogin(p.Name, p.Password)
			if err != nil {
				if err == ErrNotAllowedLogin {
					ctx.MsgWarning("该账号被禁止登陆", "")
				} else if err == ErrWrongUsername || err == ErrWrongPassword {
					ctx.MsgWarning("账号或密码错误", "")
				} else {
					ctx.ErrWarning(err)
				}
			} else {
				err = SetAdminToSession(ctx, a)
				ctx.ErrWarning(err)
				ctx.MsgSuccess("登陆成功", "")
			}
		},
		ApiDoc:&mux.ApiDoc{
			Tags:[]string{tagAdmin},
			Summary:"登陆管理员",
		},
	},

	{
		Name: "Admin Create Sub Account",
		Method:"POST",
		Route: "admin/sub-admin",
		Params:mux.Parameters{
			{
				In:mux.Form,
				Schema:&Admin{},
				Ignore:[]string{
					"ID", "Parent", "CreatedAt",
				},
			},
		},
		Handler: func(ctx *mux.Context) {
			if parent := GetAdminFromSession(ctx); parent != nil {
				p := &Admin{}
				ctx.WarnParse(p)
				if p.Name == "" {
					ctx.MsgWarning("用户名不能为空", "")
				} else if p.Password == "" {
					ctx.MsgWarning("密码不能为空", "")
				} else {
					if p.Level > parent.Level {
						ctx.MsgWarning("子帐号权限不能大于当前帐号权限: "+strconv.Itoa(parent.Level), "")
					} else {
						err := AdminCreateChild(parent.ID, p)
						if err != nil {
							if err == ErrAdminRegistered {
								ctx.MsgWarning("该用户名已注册", "")
							} else {
								ctx.ErrWarning(err)
							}
						} else {
							ctx.MsgSuccess("创建成功", "")
						}
					}
				}
			} else {
				ctx.MsgWarning("当前未登陆", "")
			}
		},
		ApiDoc:&mux.ApiDoc {
			Tags:[]string{tagAdmin},
			Summary:"创建子管理员帐号",
		},
	},

	{
		Name: "Admin Count Sub Accounts",
		Method:"GET",
		Route: "admin/count-sub-admins",
		Handler: func(ctx *mux.Context) {
			if parent := GetAdminFromSession(ctx); parent != nil {
				total := CountChildren(parent.ID)
				ctx.SendNamedJson("total", total)
			} else {
				ctx.MsgWarning("必须以管理员登陆", "")
			}
		},
		ApiDoc:&mux.ApiDoc {
			Tags:[]string{tagAdmin},
			Summary:"获取直属子管理员数量",
			Responses:mux.Responses{
				"200":{
					Description:"success",
					Example:map[string]interface{}{"total": 20},
				},
			},
		},
	},
	
	{
		Name: "Admin Sub Accounts",
		Method:"GET",
		Route: "admin/sub-admins",
		Params: mux.Parameters{
			{
				In: mux.Query,
				Schema: &getSubAccounts{},
			},
		},
		Handler: func(ctx *mux.Context) {
			if parent := GetAdminFromSession(ctx); parent != nil {
				p := &getSubAccounts{}
				ctx.WarnParse(p)
				as := GetChildren(parent.ID, p.Limit, p.Offset)
				for _, a := range as {
					a.Password = ""
				}
				ctx.SendNamedJson("list", as)
			} else {
				ctx.MsgWarning("必须以管理员登陆", "")
			}
		},
		ApiDoc:&mux.ApiDoc {
			Tags:[]string{tagAdmin},
			Summary:"获取所有子管理员信息",
			Description: "必须是管理员才能执行此操作",
			Responses:mux.Responses{
				"200":{
					Description:"success",
					Example:map[string]interface{}{"list": []Admin{{}}},
				},
			},
		},
	},

	{
		Name: "Admin Sibling Accounts",
		Method:"GET",
		Route: "admin/sibling-admins",
		Handler: func(ctx *mux.Context) {
			if a := GetAdminFromSession(ctx); a != nil {
				as := GetSiblings(a)
				for _, a := range as {
					a.Password = ""
				}
				ctx.SendNamedJson("list", as)
			} else {
				ctx.MsgWarning("必须以管理员登陆", "")
			}
		},
		ApiDoc:&mux.ApiDoc {
			Tags:[]string{tagAdmin},
			Summary:"获取所有可选择的兄弟管理员信息",
			Responses:mux.Responses{
				"200":{
					Description:"success",
					Example:map[string]interface{}{"list": []Admin{{}}},
				},
			},
		},
	},

	{
		Name: "Admin Switch Sibling Account",
		Method:"GET",
		Route: "admin/switch-sibling",
		Params:mux.Parameters{
			{
				In:mux.Query,
				Schema: &switchSiblingAccount{},
			},
		},
		Handler: func(ctx *mux.Context) {
			if a := GetAdminFromSession(ctx); a != nil {
				if a.AllowedSelect == 1 {
					p := &switchSiblingAccount{}
					ctx.WarnParse(p)
					sibling, err := GetSibling(a, p.SiblingID)
					if err != nil {
						ctx.ErrWarning(err)
					} else {
						DelAdminFromSession(ctx)
						err = SetAdminToSession(ctx, sibling)
						if err != nil {
							ctx.ErrWarning(err)
						} else {
							ctx.RedirectBack("/admin/index")
						}
					}
				} else {
					ctx.MsgWarning("当前帐号不允许切换", "")
				}
			} else {
				ctx.MsgWarning("必须以管理员登陆", "")
			}
		},
		ApiDoc:&mux.ApiDoc {
			Tags:[]string{tagAdmin},
			Summary:"切换兄弟管理员登陆",
		},
	},

	{
		Name: "Admin Update Sub Account",
		Method:"PUT",
		Route: "admin/sub-admin",
		Params:mux.Parameters{
			{
				In:mux.Form,
				Schema: &Admin{},
				Ignore: []string{"CreatedAt"},
			},
		},
		Handler: func(ctx *mux.Context) {
			if parent := GetAdminFromSession(ctx); parent != nil{
				p := &Admin{}
				ctx.WarnParse(p)
				if p.Level > parent.Level {
					ctx.MsgWarning("子帐号权限不能大于当前帐号权限: " + strconv.Itoa(parent.Level), "")
				} else {
					err := UpdateChild(p, parent)
					if err != nil {
						ctx.ErrWarning(err)
					} else {
						ctx.MsgSuccess("修改成功", "")
					}
				}
			} else {
				ctx.MsgWarning("当前未登陆", "")
			}
		},
		ApiDoc:&mux.ApiDoc{
			Tags:[]string{tagAdmin},
			Summary:"修改子管理员信息",
			Description: "如果密码不为空, 则将修改密码",
		},
	},

	{
		Name: "Admin Delete Sub Account",
		Method:"DELETE",
		Route: "admin/sub-admin",
		Params:mux.Parameters{
			{
				In:mux.Query,
				Schema: &delAdmin{},
			},
		},
		Handler: func(ctx *mux.Context) {
			if parent := GetAdminFromSession(ctx); parent != nil{
				p := &delAdmin{}
				ctx.WarnParse(p)
				err := DeleteChild(p.ID, parent)
				if err != nil {
					ctx.ErrWarning(err)
				} else {
					ctx.MsgSuccess("删除成功", "")
				}
			} else {
				ctx.MsgWarning("当前未登陆", "")
			}
		},
		ApiDoc:&mux.ApiDoc{
			Tags:[]string{tagAdmin},
			Summary:"删除子管理员",
			Description: "删除后不可恢复, 可设置禁止登陆",
		},
	},

	/**
		菜单管理
	 */
	// 获得顶级菜单
	{
		Method: "GET", Route: "admin/top-navs", Name: "Get Admin Top Navs",
		Handler: func(ctx *mux.Context) {
			admin := GetAdminFromSession(ctx)
			if admin != nil {
				tpls := mux.Handler.TplContainer.GetTopTemplates("/admin", admin.Level)
				ctx.SendNamedJson("list", tpls)
			} else {
				ctx.MsgWarning("未登陆", "")
			}
		},
		ApiDoc: &mux.ApiDoc{
			Tags: []string{tagNav},
			Summary: "获得后台顶级菜单",
			Description: "只能获得权限比登陆管理员权限低的菜单",
			Responses:mux.Responses{
				"200": {
					Description:"success",
					Example: map[string]interface{}{
						"list": []mux.Template{{}},
					},
				},
			},
		},
	},

	// 获得子菜单
	{
		Method: "GET", Route: "admin/sub-navs", Name: "Get Admin Sub Navs",
		Params:mux.Parameters{
			{
				In:mux.Query,
				Schema: &getSubNav{},
			},
		},
		Handler: func(ctx *mux.Context) {
			admin := GetAdminFromSession(ctx)
			if admin != nil {
				p := &getSubNav{}
				ctx.WarnParse(p)
				tpls := mux.Handler.TplContainer.GetSubTemplates(p.ParentLink, admin.Level)
				ctx.SendNamedJson("list", tpls)
			} else {
				ctx.MsgWarning("未登陆", "")
			}
		},
		ApiDoc: &mux.ApiDoc{
			Tags: []string{tagNav},
			Summary: "获得后台子菜单",
			Description: "只能获得权限比登陆管理员权限低的菜单",
			Responses:mux.Responses{
				"200": {
					Description:"success",
					Example: map[string]interface{}{
						"list": []mux.Template{{}},
					},
				},
			},
		},
	},

	// 获得站点地图
	{
		Method: "GET", Route: "admin/all-navs", Name: "Get Admin All Navs",
		Handler: func(ctx *mux.Context) {
			admin := GetAdminFromSession(ctx)
			if admin != nil {
				tpls := mux.Handler.TplContainer.GetAllTemplates(admin.Level)
				ctx.SendNamedJson("list", tpls)
			} else {
				ctx.MsgWarning("未登陆", "")
			}
		},
		ApiDoc: &mux.ApiDoc{
			Tags: []string{tagNav},
			Summary: "获得站点地图",
			Description: "只能获得权限比登陆管理员权限低的菜单",
			Responses:mux.Responses{
				"200": {
					Description:"success",
					Example: map[string]interface{}{
						"list": []mux.Template{{}},
					},
				},
			},
		},
	},
}

type (
	getHashedData struct {
		Data string
	}
	loginAdmin struct {
		Name string `desc:"管理员账号" required:"管理员账号不能为空"`
		Password string `desc:"管理员密码" required:"密码不能为空"`
	}
	updateChild struct {
		ChildName string `desc:"所属的子帐号" required:"子帐号不能为空"`
		Password string
		AllowedLogin bool
	}
	updatePassword struct {
		VerificationCode string `desc:"邮箱收到的验证码" required:"邮箱验证码不能为空"`
		Password string `desc:"管理员密码" required:"密码不能为空"`
	}
	sendVerified struct {
		Name string `desc:"用户帐号，修改密码时使用"`
		Email string `desc:"用户邮箱，创建帐号时使用"`
	}
	delAdmin struct {
		ID int `required:"ID 不能为空"`
	}

	delNav struct {
		ID int `required:"ID 不能为空"`
	}

	getSubNav struct {
		ParentLink string `required:"ParentLink 不能为空"`
	}
	getSubAccounts struct {
		Limit int `required:"limit 不能为空" num:"1-30" num-msg:"1-30 之间"`
		Offset int `required:"offset 不能为空"`
	}
	switchSiblingAccount struct {
		SiblingID int `required:"SiblingID 不能为空"`
	}
)

type container struct {
	admins map[string]*Admin
	sync.Mutex
}

func (c *container) Get(sessionID string) *Admin {
	c.Lock()
	defer c.Unlock()
	return c.admins[sessionID]
}

func (c *container) Set(sessionID string, admin *Admin) {
	c.Lock()
	defer c.Unlock()
	c.admins[sessionID] = admin
}

func (c *container) Del(sessionID string) {
	c.Lock()
	defer c.Unlock()
	delete(c.admins, sessionID)
}

var Container = &container{admins: make(map[string]*Admin, 20)}

func GetAdminFromSession(ctx *mux.Context) *Admin {
	id := session.HttpCookeSessionID(ctx.Response, ctx.Request)
	if a := Container.Get(id); a != nil {
		return a
	} else {
		str := ctx.Session().Get("admin")
		if str == "" {
			return nil
		} else {
			a = &Admin{}
			err := json.Unmarshal([]byte(str), a)
			if err != nil {
				log.Emergency.Println(trace.Error(err))
				return nil
			} else {
				Container.Set(id, a)
				return a
			}
		}
	}
}

func DelAdminFromSession(ctx *mux.Context) {
	session := ctx.Session()
	session.Del("admin")
	Container.Del(session.ID())
}

func SetAdminToSession(ctx *mux.Context, a *Admin) error {
	data, err := json.Marshal(a)
	if err != nil {
		return err
	}
	session := ctx.Session()
	session.Set("admin", string(data))
	Container.Set(session.ID(), a)
	return nil
}